package org.apollo.swing.componentc;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Transparency;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.accessibility.AccessibleContext;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonModel;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;

import sun.awt.SunToolkit;
import sun.swing.SwingUtilities2;
import org.apollo.swing.component.JImagePane;
import org.apollo.swing.layout.LineLayout;
import org.apollo.swing.util.UIResourceManager;
import org.apollo.swing.util.UIUtil;
import org.apollo.swing.util.Util;

class CTitlePane extends JComponent
{
    private static final long serialVersionUID = -8078184400171961600L;
    
    private static final Font TITLE_FONT_CH = UIUtil.getTitleFont(true);
    
    private static final Font TITLE_FONT_EN = UIUtil.getTitleFont(false);

    private static final int IMAGE_HEIGHT = 16;

    private static final int IMAGE_WIDTH = 16;
    
    private static final int HALO_SIZE = 12;
    
    private static final float SHADOW_ALPHA_STEP = 0.075f;

    private PropertyChangeListener propertyChangeListener;

    private Action closeAction;

    private Action iconifyAction;

    private Action restoreAction;

    private Action maximizeAction;

    private JCButton btnMaxOrRestore;

    private JCButton btnIconify;

    private JCButton btnClose;
    
    private JImagePane contentPane;

    private Image systemIcon;
    
    private Image shadowImage;
    
    private String title;
    
    private int textX;
    
    private int textY;
    
    private String[] titleChars;
    
    private boolean[] chFlags;

    private WindowListener windowListener;

    private Window window;

    private JRootPane rootPane;

    private int state;

    private CRootPaneUI rootPaneUI;

    public CTitlePane(JRootPane root, CRootPaneUI ui)
    {
        this.rootPane = root;
        rootPaneUI = ui;
        state = -1;
        setLayout(new LineLayout(0, LineLayout.LEADING, LineLayout.LEADING, LineLayout.HORIZONTAL));
        installSubcomponents();
        setPreferredSize(new Dimension(-1, 28));
        setOpaque(false);
    }

    private void installListeners()
    {
        if(window != null)
        {
            windowListener = createWindowListener();
            window.addWindowListener(windowListener);
            propertyChangeListener = createWindowPropertyChangeListener();
            window.addPropertyChangeListener(propertyChangeListener);
        }
    }

    private void uninstallListeners()
    {
        if(window != null)
        {
            window.removeWindowListener(windowListener);
            window.removePropertyChangeListener(propertyChangeListener);
        }
    }

    private WindowListener createWindowListener()
    {
        return new WindowHandler();
    }

    private PropertyChangeListener createWindowPropertyChangeListener()
    {
        return new PropertyChangeHandler();
    }

    public JRootPane getRootPane()
    {
        return rootPane;
    }

    private int getWindowDecorationStyle()
    {
        return rootPane.getWindowDecorationStyle();
    }

    public void addNotify()
    {
        super.addNotify();
        uninstallListeners();
        window = SwingUtilities.getWindowAncestor(this);
        
        if(window != null)
        {
            if(window instanceof Frame)
            {
                setState(((Frame)window).getExtendedState());
            }
            else
            {
                setState(0);
            }

            setActive(window.isActive());
            installListeners();
            updateSystemIcon();
        }
    }

    public void removeNotify()
    {
        super.removeNotify();
        uninstallListeners();
        window = null;
    }

    private void installSubcomponents()
    {
        int decorationStyle = getWindowDecorationStyle();
        
        if(decorationStyle == JRootPane.FRAME)
        {
            createActions();
            createButtons();
            add(btnIconify, LineLayout.END);
            add(btnMaxOrRestore, LineLayout.END);
            add(btnClose, LineLayout.END);
        }
        else if(decorationStyle != JRootPane.NONE)
        {
            createActions();
            createButtons();
            add(btnClose, LineLayout.END);
        }
        
        contentPane = new JImagePane();
        contentPane.setBackground(UIResourceManager.getEmptyColor());
        contentPane.setImageOnly(true);
        add(contentPane, LineLayout.MIDDLE_FILL);
    }

    private void close()
    {
        if(window != null)
        {
            window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
        }
    }

    private void iconify()
    {
        Frame frame = getFrame();
        
        if(frame != null)
        {
            frame.setExtendedState(state | Frame.ICONIFIED);
        }
    }

    private void maximize()
    {
        Frame frame = getFrame();
        
        if(frame != null)
        {
            frame.setExtendedState(state | Frame.MAXIMIZED_BOTH);
        }
    }

    private void restore()
    {
        Frame frame = getFrame();

        if(frame == null)
        {
            return;
        }

        if((state & Frame.ICONIFIED) != 0)
        {
            frame.setExtendedState(state & ~Frame.ICONIFIED);
        }
        else
        {
            frame.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
        }
    }

    private void createActions()
    {
        closeAction = new CloseAction();
        
        if(getWindowDecorationStyle() == JRootPane.FRAME)
        {
            iconifyAction = new IconifyAction();
            restoreAction = new RestoreAction();
            maximizeAction = new MaximizeAction();
        }
    }

    private void createButtons()
    {
        btnClose = new JCButton(closeAction);
        btnIconify = new JCButton(iconifyAction);
        btnMaxOrRestore = new JCButton(restoreAction);
        JCButton[] buttons = new JCButton[]{btnClose, btnIconify, btnMaxOrRestore};
        String[] names = {"Close", "Iconify", "Maximize"};
        int index = 0;
        
        btnClose.setImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_CLOSE_IMAGE));
        btnClose.setRolloverImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_CLOSE_ROLLOVER_IMAGE));
        btnClose.setPressedImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_CLOSE_PRESSED_IMAGE));
        btnClose.setPreferredSize(new Dimension(btnClose.getImage().getWidth(null), btnClose.getImage().getHeight(null)));
        
        btnIconify.setImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_MIN_IMAGE));
        btnIconify.setRolloverImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_MIN_ROLLOVER_IMAGE));
        btnIconify.setPressedImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_MIN_PRESSED_IMAGE));
        btnIconify.setPreferredSize(new Dimension(btnIconify.getImage().getWidth(null), btnIconify.getImage().getHeight(null)));
        
        for(JCButton button: buttons)
        {
            button.setToolTipText(button.getText());
            button.setText(null);
            button.setFocusable(false);
            button.setFocusPainted(false);
            button.putClientProperty("paintActive", Boolean.TRUE);
            button.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, names[index++]);
        }
    }

    private void setActive(boolean isActive)
    {
        btnClose.putClientProperty("paintActive", isActive);
        
        if(getWindowDecorationStyle() == JRootPane.FRAME)
        {
            btnIconify.putClientProperty("paintActive", isActive);
            btnMaxOrRestore.putClientProperty("paintActive", isActive);
        }
        
        rootPane.repaint();
    }

    private void setState(int state)
    {
        setState(state, false);
    }

    private void setState(int state, boolean updateRegardless)
    {
        if(window != null && getWindowDecorationStyle() == JRootPane.FRAME && (this.state != state || updateRegardless))
        {
            Frame frame = getFrame();

            if(frame != null)
            {
                if(((state & Frame.MAXIMIZED_BOTH) != 0) && frame.isShowing())
                {
                    rootPaneUI.uninstallBorder(rootPane);
                }
                else if((state & Frame.MAXIMIZED_BOTH) == 0)
                {
                    rootPaneUI.installBorder(rootPane);
                }
                
                if(frame.isResizable())
                {
                    if((state & Frame.MAXIMIZED_BOTH) != 0)
                    {
                        updateRestoreButton(restoreAction);
                        maximizeAction.setEnabled(false);
                        restoreAction.setEnabled(true);
                    }
                    else
                    {
                        updateRestoreButton(maximizeAction);
                        maximizeAction.setEnabled(true);
                        restoreAction.setEnabled(false);
                    }
                    
                    if(btnMaxOrRestore.getParent() == null || btnIconify.getParent() == null)
                    {
                        btnMaxOrRestore.setVisible(true);
                        btnIconify.setVisible(true);
                        revalidate();
                        repaint();
                    }
                }
                else
                {
                    maximizeAction.setEnabled(false);
                    restoreAction.setEnabled(false);
                    
                    if(btnMaxOrRestore.getParent() != null)
                    {
                        btnMaxOrRestore.setVisible(false);
                        revalidate();
                        repaint();
                    }
                }
            }
            else
            {
                maximizeAction.setEnabled(false);
                restoreAction.setEnabled(false);
                iconifyAction.setEnabled(false);
                btnMaxOrRestore.setVisible(false);
                btnIconify.setVisible(false);
                revalidate();
                repaint();
            }
            
            closeAction.setEnabled(true);
            this.state = state;
        }
    }

    private void updateRestoreButton(Action action)
    {
        if(action == restoreAction)
        {
            btnMaxOrRestore.setImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_RESTORE_IMAGE));
            btnMaxOrRestore.setRolloverImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_RESTORE_ROLLOVER_IMAGE));
            btnMaxOrRestore.setPressedImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_RESTORE_PRESSED_IMAGE));
            btnMaxOrRestore.setPreferredSize(new Dimension(btnMaxOrRestore.getImage().getWidth(null),
                            btnMaxOrRestore.getImage().getHeight(null)));
        }
        else
        {
            btnMaxOrRestore.setImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_MAX_IMAGE));
            btnMaxOrRestore.setRolloverImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_MAX_ROLLOVER_IMAGE));
            btnMaxOrRestore.setPressedImage(UIResourceManager.getImage(UIResourceManager.KEY_WINDOW_MAX_PRESSED_IMAGE));
            btnMaxOrRestore.setPreferredSize(new Dimension(btnMaxOrRestore.getImage().getWidth(null), btnClose.getImage().getHeight(null)));
        }
        
        btnMaxOrRestore.setAction(action);
        btnMaxOrRestore.setToolTipText(btnMaxOrRestore.getText());
        btnMaxOrRestore.setText(null);
    }

    public void paintComponent(Graphics g)
    {
        Frame frame = getFrame();
        String title = getTitle();
        int x = 5;
        int y = 5;
        
        if(systemIcon != null)
        {
            g.drawImage(systemIcon, x, y, IMAGE_WIDTH, IMAGE_HEIGHT, rootPane);
            x += IMAGE_WIDTH + 3;
        }
        
        if(title != null && !title.trim().isEmpty())
        {
            if(!title.equals(this.title))
            {
                this.title = title;
                createTitleShadow(g);
            }
            
            int shadowX = x - 3;
            int shadowY = (this.getHeight() - shadowImage.getHeight(rootPane)) / 2;
            x = textX;
            
            g.translate(shadowX, shadowY);
            g.setColor(rootPane.getForeground());
            g.drawImage(shadowImage, 0, 0, rootPane);
            
            for(int i = 0; i < title.length(); i++)
            {
                g.setFont(chFlags[i]? TITLE_FONT_CH: TITLE_FONT_EN);
                SwingUtilities2.drawString(rootPane, g, titleChars[i], x, textY);
                x += g.getFontMetrics().stringWidth(titleChars[i]);
            }
            
            g.translate(-shadowX, -shadowY);
        }
        
        if(frame != null)
        {
            setState(frame.getExtendedState());
        }
    }
    
    private Frame getFrame()
    {
        if(window instanceof Frame)
        {
            return (Frame)window;
        }
        
        return null;
    }

    private String getTitle()
    {
        if(window instanceof Frame)
        {
            return ((Frame)window).getTitle();
        }
        
        else if(window instanceof Dialog)
        {
            return ((Dialog)window).getTitle();
        }
        
        return null;
    }
    
    public JImagePane getContentPane()
    {
        return contentPane;
    }
    
    private void createTitleShadow(Graphics g)
    {
        int titleLength = title.length();
        Map<Shape, Integer> shapeMap = new LinkedHashMap<Shape, Integer>();
        titleChars = new String[titleLength];
        chFlags = new boolean[titleLength];
        Stroke stroke;
        int shadowX = 0;
        int shadowY = 0;
        int shadowWidth = 0;
        int shadowHeight = 0;
        int textY = 0;
        int textWidth = 0;
        int textHeight = 0;
        
        for(int i = 0; i < titleLength; i++)
        {
            titleChars[i] = String.valueOf(title.charAt(i));
            chFlags[i] = Util.isChinese(titleChars[i]);
            Font font = chFlags[i]? TITLE_FONT_CH: TITLE_FONT_EN;
            FontMetrics fm = g.getFontMetrics(font);
            GlyphVector vector = font.createGlyphVector(fm.getFontRenderContext(), titleChars[i]);
            stroke = new BasicStroke(HALO_SIZE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            Shape textShape = vector.getOutline();
            Shape shadowShape = stroke.createStrokedShape(textShape);
            Rectangle textRect = textShape.getBounds();
            Rectangle shadowRect = shadowShape.getBounds();
            shadowY = Math.min(shadowRect.y, shadowY);
            textY = Math.min(textRect.y, textY);
            shadowHeight = Math.max(shadowRect.y + shadowRect.height, shadowHeight);
            textHeight = Math.max(textRect.y + textRect.height, textHeight);
            
            if(i == titleLength -1)
            {
                shadowWidth = shadowX + shadowRect.width;
            }
            
            shapeMap.put(shadowShape, shadowX);
            shadowX += fm.stringWidth(titleChars[i]);
        }
        
        shadowHeight -= shadowY;
        textWidth = shadowX;
        textHeight -= textY;
        GraphicsConfiguration gc = UIUtil.getGraphicsConfiguration(this);
        BufferedImage image = gc.createCompatibleImage(shadowWidth, shadowHeight, Transparency.TRANSLUCENT);
        Graphics2D imageG2D = (Graphics2D)image.createGraphics();
        imageG2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        imageG2D.setColor(Color.WHITE);
        
        for(Shape shape: shapeMap.keySet())
        {
            Rectangle rect = shape.getBounds();
            int deltaX = shapeMap.get(shape);
            imageG2D.translate(-rect.x + deltaX, -shadowY);
            imageG2D.fill(shape);
            imageG2D.translate(rect.x - deltaX, shadowY);
        }
        
        imageG2D.dispose();
        
        BufferedImage shadowImage = gc.createCompatibleImage(shadowWidth, shadowHeight, Transparency.TRANSLUCENT);
        imageG2D = (Graphics2D)shadowImage.createGraphics();
        int width = shadowWidth;
        int height = shadowHeight;
        int x = 0;
        int y = 0;
        imageG2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        for(int i = 0; i < HALO_SIZE; i++)
        {
            imageG2D.setComposite(AlphaComposite.SrcOver.derive(SHADOW_ALPHA_STEP));
            imageG2D.drawImage(image, x, y, width, height, null);
            x++;
            y++;
            width -= 2;
            height -= 2;
        }
        
        imageG2D.dispose();
        this.textX = Math.round((shadowWidth - textWidth) / 2.0f);
        this.textY = -textY + Math.round((shadowHeight - textHeight) / 2.0f) + (1 - (shadowHeight - textHeight) % 2);
        this.shadowImage = shadowImage;
    }

    private void updateSystemIcon()
    {
        if(window == null)
        {
            systemIcon = null;
            return;
        }
        
        List<Image> icons = window.getIconImages();
        
        if(icons == null || icons.isEmpty())
        {
            systemIcon = null;
            return;
        }

        if(icons.size() == 1)
        {
            systemIcon = icons.get(0);
        }
        else
        {
            systemIcon = SunToolkit.getScaledIconImage(icons, IMAGE_WIDTH, IMAGE_HEIGHT);
        }
    }
    
    void changeCloseButtonState()
    {
        ButtonModel model = btnClose.getModel();
        Point mouseLocation = Util.getMouseLocation();
        Point buttonLocation = btnClose.getLocationOnScreen();
        int x = mouseLocation.x - buttonLocation.x;
        int y = mouseLocation.y - buttonLocation.y;
        
        if(model.isRollover() && (mouseLocation.x > buttonLocation.x + btnClose.getWidth() || mouseLocation.y < buttonLocation.y))
        {
            btnClose.dispatchEvent(new MouseEvent(btnClose, MouseEvent.MOUSE_EXITED, System.currentTimeMillis(), 0, x, y, 0, false));
        }
        else if(!model.isPressed() && !model.isRollover() && btnClose.getBounds().contains(btnClose.getX() + x, btnClose.getY() + y))
        {
            btnClose.dispatchEvent(new MouseEvent(btnClose, MouseEvent.MOUSE_ENTERED, System.currentTimeMillis(), 0, x, y, 0, false));
        }
    }
    
    private class CloseAction extends AbstractAction
    {
        private static final long serialVersionUID = -1022852041948761832L;

        public CloseAction()
        {
            super("\u5173\u95ED");
        }

        public void actionPerformed(ActionEvent e)
        {
            close();
        }
    }

    private class IconifyAction extends AbstractAction
    {
        private static final long serialVersionUID = -1509739185778590162L;

        public IconifyAction()
        {
            super("\u6700\u5C0F\u5316");
        }

        public void actionPerformed(ActionEvent e)
        {
            iconify();
        }
    }

    private class RestoreAction extends AbstractAction
    {
        private static final long serialVersionUID = 7027403948364374755L;

        public RestoreAction()
        {
            super("\u8FD8\u539F");
        }

        public void actionPerformed(ActionEvent e)
        {
            restore();
        }
    }

    private class MaximizeAction extends AbstractAction
    {
        private static final long serialVersionUID = -7252759733748229590L;

        public MaximizeAction()
        {
            super("\u6700\u5927\u5316");
        }

        public void actionPerformed(ActionEvent e)
        {
            maximize();
        }
    }

    private class PropertyChangeHandler implements PropertyChangeListener
    {
        public void propertyChange(PropertyChangeEvent e)
        {
            String name = e.getPropertyName();

            if("resizable".equals(name) || "state".equals(name))
            {
                Frame frame = getFrame();

                if(frame != null)
                {
                    setState(frame.getExtendedState(), true);
                }
                
                if("resizable".equals(name))
                {
                    rootPane.repaint();
                }
            }
            else if("title".equals(name))
            {
                repaint();
            }
            else if("iconImage" == name)
            {
                updateSystemIcon();
                repaint();
            }
        }
    }

    private class WindowHandler extends WindowAdapter
    {
        public void windowActivated(WindowEvent ev)
        {
            setActive(true);
        }

        public void windowDeactivated(WindowEvent ev)
        {
            setActive(false);
        }
    }
}